﻿#include "server/serverthread.h"
#include "json/json.h"
#include "utils.h"
#include "valdefs.h"
#include "server/serverwindow.h"

#include <QDebug>
#include <string>
#ifdef WIN32
#include <winsock2.h>
#else
#include <cstdio>
#include <iostream>
#include <cstring>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#endif
#include <fstream>
#include <iostream>
#include <QMessageBox>
#include <ctime>
#include <QCoreApplication>


ServerThread::ServerThread(ServerWindow *sw_, QObject *parent, std::string server_config_filepath) : QThread(parent){
    this->sw_ = sw_;
    QMessageBox::information((QWidget*)sw_, tr("注意！"), tr("开启服务器进程！\n由于OpenSSL生成新密钥需要一定的时间，可能会出现有几秒没有响应的状况，请耐心等待"));
    InitConn();
    this->flag_softly_exit_ = false;
    rsa_prikey_ = "\0";
    rsa_pubkey_ = "\0";
    this->SetServerConfigFilepath(server_config_filepath);
    this->ReadServerConfig();
    client_socket_fd_ = -1;
    server_socket_fd_ = -1;
    this->GenerateRSAKey();
    QMessageBox::information((QWidget*)sw_, tr("注意！"), tr("密钥对准备完毕，服务器进程就绪！"));
}


void ServerThread::InitConn(void){
    connect(this, &ServerThread::SignalRequestExit, this, &ServerThread::OnRequestExit);
    connect(this, &ServerThread::finished, this, &ServerThread::OnAboutFinished);
    connect(this, &ServerThread::SignalWindowShowMsg, this, &ServerThread::OnWindowShowMsg);
    connect(this, &ServerThread::SignalChangeConfig, this, &ServerThread::OnChangeConfig);
    connect(this, &ServerThread::SignalSoftRestart, this, &ServerThread::OnSoftRestart);
    connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &ServerThread::OnAppQuit);
}


void ServerThread::run(void){

    std::string msg;
    msg = "Server thread ready to start!\nPubkey:\n"+rsa_pubkey_+"\nPrikey:\n"+rsa_prikey_;
    emit this->SignalWindowShowMsg(QString::fromStdString(msg));
    ShowConfig();

    int                 ret;
    int                 sock_len;
    std::string         readstr;
    std::string         writebuf;

    unsigned int        server_socket_fd;
    unsigned int        client_socket_fd;
    struct sockaddr_in  server_addr, client_addr;

#if WIN32
    unsigned short      sock_version;
    struct WSAData      wsa_data;
    sock_version = MAKEWORD(2, 2);
    if(WSAStartup(sock_version, &wsa_data)!=0) {
        emit sw_->SignalShowMsg("Failed to startup WSA!");
        this->terminate();
    }
    server_socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#else
    server_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
#endif
    this->server_socket_fd_ = server_socket_fd;

    if(server_socket_fd == -1){
        qDebug()<<"Socket error!";
        exit(-1);
    }

    memset(&server_addr, 0x00, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = this->server_config_.listening_port;

    if(bind(server_socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr))!=0){
        qDebug()<<"Bind falied!";
        exit(-1);
    }

    if(listen(server_socket_fd, 5)!= 0){
        qDebug()<<"Listen falied!";
        exit(-1);
    }

    sock_len = sizeof(struct sockaddr_in);

    while(!flag_softly_exit_){
        client_socket_fd = accept(server_socket_fd, (struct sockaddr *)&client_addr, &sock_len);
        this->client_socket_fd_ = client_socket_fd;

        if(client_socket_fd == 0){
            qDebug()<<"Connect failed!";
            continue;
        }

        qDebug()<<"Connection established!";
        char* readbuf = new char[512];
        memset(readbuf, 0x00, 512);
        ret = recv(client_socket_fd, readbuf, sizeof(char)*511, 0);
        readbuf[ret] = 0x00;
        risked_cstr_ptrs_.push_back(readbuf);
        readstr = std::string(readbuf, ret+1);
        delete[] readbuf;
        risked_cstr_ptrs_.pop_back();

        if(ret <= 0){
            qDebug()<<"Failed to get msg!";
        }else{
            qDebug()<<"Got msg bytes: ["<< utils_rsa::StrToHexStr(readstr).c_str() <<"]";
            HandleMsg(readstr, client_socket_fd);
        }

        closesocket(client_socket_fd);
    }
    closesocket(server_socket_fd);
#if WIN32
    WSACleanup();
#endif
    exit(0);
}


void ServerThread::HandleMsg(std::string msg, unsigned int client_socket_fd) {
    Json::Reader reader;
    Json::Value root;
    std::string decrypted_msg;
    if(!reader.parse(msg, root)){
        utils_rsa::RsaDecryptUsingPrikey(rsa_prikey_, msg, decrypted_msg);
        if(reader.parse(decrypted_msg, root)){
            if(root.isNull()){
                emit sw_->SignalShowWarning("New msg is null!");
                std::string ret_msg = PARSE_FAILURE;
                utils_sock::SendMsg(client_socket_fd, ret_msg);
                return;
            }
            else if(root["type"].isNull()){
                emit sw_->SignalShowWarning("New msg has a null type!");
                std::string ret_msg = PARSE_FAILURE;
                utils_sock::SendMsg(client_socket_fd, ret_msg);
                return;
            }
            else if(!root["type"].isConvertibleTo(Json::intValue)){
                emit sw_->SignalShowWarning("New msg's type is not a num!");
                std::string ret_msg = PARSE_FAILURE;
                utils_sock::SendMsg(client_socket_fd, ret_msg);
                return;
            }
            int type = root["type"].asInt();
            Json::Value val = root["val"];
            switch(type){
            case TYPE_MSG:
                HandleMsgType(val, client_socket_fd);
                break;
            case TYPE_REQ_REG:
                HandleRegReq(val, client_socket_fd);
                break;
            case TYPE_REQ_UNREG:
                HandleUnregReq(val, client_socket_fd);
                break;
            case TYPE_REFRESH_LOGIN:
                HandleLoginRefresh(val, client_socket_fd);
                break;
            case TYPE_LOGIN:
                HandleLogin(val, client_socket_fd);
                break;
            case TYPE_LOGOUT:
                HandleLogout(val, client_socket_fd);
            default:
                emit sw_->SignalShowWarning("New msg has no supported type!");
                std::string ret_msg = TYPE_NOT_SUPPORTED;
                utils_sock::SendMsg(client_socket_fd, ret_msg);
                return;
                break;
            }
        }else{
            emit sw_->SignalShowWarning("New msg is NEITHER clear msg NOR decrypted json!");
            std::string ret_msg = PARSE_FAILURE;
            utils_sock::SendMsg(client_socket_fd, ret_msg);
            return;
        }
    }else{
        if(root.isNull()){
            emit sw_->SignalShowWarning("New msg is null!");
            std::string ret_msg = PARSE_FAILURE;
            utils_sock::SendMsg(client_socket_fd, ret_msg);
            return;
        }
        else if(root["type"].isNull()){
            emit sw_->SignalShowWarning("New msg has a null type!");
            std::string ret_msg = PARSE_FAILURE;
            send(client_socket_fd, ret_msg.c_str(), ret_msg.size(), 0);
            return;
        }
        else if(!root["type"].isConvertibleTo(Json::intValue)){
            emit sw_->SignalShowWarning("New msg has no type info!");
            std::string ret_msg = PARSE_FAILURE;
            utils_sock::SendMsg(client_socket_fd, ret_msg);
            return;
        }
        int type = root["type"].asInt();
        switch(type){
        case TYPE_REQ_PUBKEY:
            break;
        default:
            emit sw_->SignalShowWarning("New msg is uncrypted and is not a pubkey requrest!");
            std::string ret_msg = NOT_ENCRYPTED;
            utils_sock::SendMsg(client_socket_fd, ret_msg);
            return;
            break;
        }
    }
}


void ServerThread::OnRequestExit(void){
    this->flag_softly_exit_ = true;
}


void ServerThread::GenerateRSAKey(){
    utils_rsa::GenerateRsaKey(rsa_prikey_, rsa_pubkey_, KEY_LEN);
}


bool ServerThread::SaveServerConfig(void){
    Json::StyledWriter  writer;
    Json::Value         root;
    std::ofstream       fout;
    std::string         json_text;

    root["port"] = this->server_config_.listening_port;

    json_text = writer.write(root);

    fout.open(this->server_config_filepath_, std::ios::trunc);
    fout << json_text << std::endl;
    fout.close();
    return true;
}


bool ServerThread::ReadServerConfig(void){
    Json::Reader        reader;
    Json::Value         root;
    std::ifstream       fin;
    std::string         json_text;
    std::stringstream   ss;

    try{
        qDebug()<<"Reading file: " << this->server_config_filepath_.c_str();
        fin.open(this->server_config_filepath_, std::ios::in);
        ss << fin.rdbuf();
        json_text = ss.str();
    } catch (std::exception e) {
        qWarning() << e.what();
        return false;
    }

    if(!reader.parse(json_text, root) ){
        qWarning("Failed to parse file to json!");
        server_config_.listening_port = DEFAULT_SERVER_PORT;
        return false;
    }

    struct ServerConfig server_config;
    server_config.listening_port = root["port"].asUInt();
    this->server_config_ = server_config;
    return true;
}


void ServerThread::SetServerConfigFilepath(std::string filepath){
    this->server_config_filepath_ = filepath;
}


ServerThread::~ServerThread(void){
    this->SaveServerConfig();
    TryToCloseNetwork();
    for(int i = 0; i < risked_cstr_ptrs_.size(); i++){
        delete[] risked_cstr_ptrs_[i];
    }
}


void ServerThread::TryToCloseNetwork(void){
    int error = 0;
    int len = sizeof(error);
    getsockopt(this->client_socket_fd_, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len);
    if (error != 0){
        qDebug()<<"Client socket is not closed, closing...";
        closesocket(this->client_socket_fd_);
    }
    getsockopt(this->server_socket_fd_, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len);
    if (error != 0){
        qDebug()<<"Server socket is not closed, closing...";
        closesocket(this->server_socket_fd_);
    }
#ifdef WIN32
    WSACleanup();
#endif
}


void ServerThread::OnAboutFinished(){
    TryToCloseNetwork();
    SaveServerConfig();
}


void ServerThread::OnWindowShowMsg(QString msg){
    emit sw_->SignalShowMsg(msg);
}


void ServerThread::OnChangeConfig(struct ServerConfig config){
    std::string msg;

    msg = "Config changed. Restarting...";
    emit this->SignalWindowShowMsg(QString::fromStdString(msg) );

    this->terminate();
    TryToCloseNetwork();
    this->server_config_ = config;

    this->start();

    msg = "Restarted successfully!";
    emit this->sw_->SignalShowMsg(QString::fromStdString(msg) );
}


void ServerThread::ShowConfig(void){
    std::stringstream ss;
    ss << "--\nServer Config:\nPort:" << this->server_config_.listening_port<<"\n--";
    emit this->sw_->SignalShowMsg(QString::fromStdString(ss.str() ) );
}


void ServerThread::OnSoftRestart(void){
    std::stringstream ss;
    ss << "Soft restarting...";
    emit this->sw_->SignalShowMsg(QString::fromStdString(ss.str() ) );
    ss.str("");
    ss.clear();
    this->terminate();
    this->start();
    ss << "Restarted successfully!";
    emit this->sw_->SignalShowMsg(QString::fromStdString(ss.str()));
}


void ServerThread::OnAppQuit(){
    if(!isFinished()){
        terminate();
    }
    TryToCloseNetwork();
    SaveServerConfig();
}


void ServerThread::HandleMsgType(Json::Value json, unsigned int client_socket_fd){
    if((!json["self_id"].isNull()) && (!json["target_id"].isNull()) && (!json["msg"].isNull()) && (!json["password"].isNull())){
        if(json["self_id"].isConvertibleTo(Json::uintValue) && json["target_id"].isConvertibleTo(Json::uintValue)){
            unsigned long long self_id, target_id;
            std::string msg, password;
            self_id = json["self_id"].asInt64();
            target_id = json["target_id"].asUInt64();
            msg = json["msg"].asString();
            password = json["password"].asString();
            auto self_itr = registered_client_map_.find(self_id);
            auto target_itr = registered_client_map_.find(target_id);
            auto target_login_itr = online_client_map_.find(target_id);
            if(self_itr != registered_client_map_.end()){
                if(target_itr != registered_client_map_.end()){
                    if(target_login_itr != online_client_map_.end()){
                        std::string target_ip = target_login_itr->second.ip;
                        unsigned short target_port = target_login_itr->second.port;
                        unsigned int target_sock = utils_sock::CreateClientSockFd(target_ip, target_port);
                        if(target_sock != 0) {
                            utils_sock::SendRsaPubkeyEncryptedMsg(target_sock, target_itr->second.pubkey, msg);
                            utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, self_itr->second.pubkey, SUCCESS);
                        } else {
                            utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, self_itr->second.pubkey, SERVER_SOCK_FAILURE);
                            return;
                        }
                    }else{
                        utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, self_itr->second.pubkey, TARGET_NOT_ONLINE);
                        return;
                    }
                } else {
                    utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, self_itr->second.pubkey, TARGET_NOT_FOUND);
                    return;
                }
            } else {
                utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
                return;
            }
        }
    }
    utils_sock::SendMsg(client_socket_fd, DATA_NOT_VALID);
}


void ServerThread::HandleRegReq(Json::Value json, unsigned int client_socket_fd){
    struct RegisteredClientData new_rd;
    if(!json["id"].isNull()){
        if(json["id"].isConvertibleTo(Json::uintValue)){
            unsigned long long id = json["id"].asUInt64();
            if(!json["password"].isNull()){
                std::string password = json["password"].asString();
                if(!json["pubkey"].isNull()){
                    std::string pubkey = json["pubkey"].asString();
                    if(utils_rsa::CheckKeyType(pubkey) == utils_rsa::RSA_PUBKEY_TYPE){
                        new_rd.pubkey = pubkey;
                        new_rd.password = password;
                        new_rd.id = id;
                        this->registered_client_map_[id] = new_rd;
                        utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, pubkey, SUCCESS);
                        return;
                    }
                }
            }
        }
    }
    utils_sock::SendMsg(client_socket_fd, DATA_NOT_VALID);
    return;
}


void ServerThread::HandleUnregReq(Json::Value json, unsigned int client_socket_fd){
    if((!json["password"].isNull() ) && (!json["id"].isNull() ) ){
        if(json["id"].isConvertibleTo(Json::uintValue)){
            std::string password = json["password"].asString();
            unsigned long long id = json["id"].asUInt64();
            auto itr = registered_client_map_.find(id);
            if(itr == registered_client_map_.end()){
                utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
                return;
            }
            if(itr->second.password != password){
                utils_sock::SendMsg(client_socket_fd, INCORRECT_PASSWORD);
                return;
            }
            registered_client_map_.extract(id);
            if(online_client_map_.find(id) != online_client_map_.end()){
                online_client_map_.extract(id);
            }
            utils_sock::SendMsg(client_socket_fd, SUCCESS);
        }
    }
    utils_sock::SendMsg(client_socket_fd, DATA_NOT_VALID);
}


void ServerThread::HandlePubkeyReq(Json::Value json, unsigned int client_socket_fd){
    utils_sock::SendMsg(client_socket_fd, this->rsa_pubkey_);
    utils_sock::SendMsg(client_socket_fd, PUBKEY_CONFIRMATION_STR);
    utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, this->rsa_pubkey_, PUBKEY_CONFIRMATION_STR);
}


void ServerThread::HandleLogin(Json::Value json, unsigned int client_socket_fd){
    unsigned long long id = json["id"].asInt64();
    std::string password = json["password"].asString();
    struct OnlineClientData od;
    struct RegisteredClientData rd;
    if(FindIdInRegisteredClients(id, rd)){
        if(rd.password == password){
            if(FindIdInOnlineClients(id, od)){
                utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, rd.pubkey, ALREADY_LOGGED_IN);
            } else {
                online_client_map_.extract(id);
                utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, rd.pubkey, SUCCESS);
            }
        } else {
            utils_sock::SendMsg(client_socket_fd, INCORRECT_PASSWORD);
        }
    } else {
        utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
    }
}


void ServerThread::HandleLogout(Json::Value json, unsigned int client_socket_fd){
    unsigned long long id = json["id"].asInt64();
    std::string password = json["password"].asString();
    struct OnlineClientData od;
    struct RegisteredClientData rd;
    if(FindIdInRegisteredClients(id, rd)){
        if(rd.password == password){
            if(!FindIdInOnlineClients(id, od)){
                utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, rd.pubkey, ALREADY_LOGGED_OUT);
            }else{
                online_client_map_.extract(id);
                utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, rd.pubkey, SUCCESS);
            }
        } else {
            utils_sock::SendMsg(client_socket_fd, INCORRECT_PASSWORD);
        }
    } else {
        utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
    }
}


void ServerThread::HandleLoginRefresh(Json::Value json, unsigned int client_socket_fd){
    unsigned long long id = json["id"].asInt64();
    std::string password = json["password"].asString();

    struct sockaddr_in sockname;
    int namelen;

    getsockname(client_socket_fd, (struct sockaddr*)&sockname, &namelen);

    std::string new_ip = std::string(inet_ntoa(sockname.sin_addr) );
    unsigned short new_port = ntohs(sockname.sin_port);

    struct OnlineClientData od;
    struct RegisteredClientData rd;
    if(FindIdInRegisteredClients(id, rd)){
        if(rd.password == password){
            online_client_map_.at(id).ip = new_ip;
            online_client_map_.at(id).port = new_port;
            utils_sock::SendMsg(client_socket_fd, SUCCESS);
        } else {
            utils_sock::SendMsg(client_socket_fd, TARGET_NOT_FOUND);
        }
    } else {
        utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
    }
}


void ServerThread::HandleClientPubkeyChange(Json::Value json, unsigned int client_socket_fd){
    unsigned long long id = json["id"].asInt64();
    std::string password = json["password"].asString();
    std::string new_pubkey = json["pubkey"].asString();
    auto itr = registered_client_map_.find(id);
    if(itr != registered_client_map_.end()){
        if(itr->second.password == password){
            itr->second.pubkey = new_pubkey;
            utils_sock::SendRsaPubkeyEncryptedMsg(client_socket_fd, new_pubkey, SUCCESS);
        } else {
            utils_sock::SendMsg(client_socket_fd, INCORRECT_PASSWORD);
        }
    } else {
        utils_sock::SendMsg(client_socket_fd, ID_CHECK_FAILURE);
    }
}


bool ServerThread::FindIdInRegisteredClients(unsigned long long id, struct RegisteredClientData &ret){
    auto iter = registered_client_map_.find(id);
    if (iter == registered_client_map_.end()){
        return false;
    }else{
        ret = iter->second;
        return true;
    }
}


bool ServerThread::FindIdInOnlineClients(unsigned long long id, struct OnlineClientData &ret){
    auto iter = online_client_map_.find(id);
    if (iter == online_client_map_.end()){
        return false;
    }else{
        ret = iter->second;
        return true;
    }
}
